package interval

import (
	"errors"
	"strconv"
	"strings"
	"time"
)

// Next 返回“从 t 开始”的下一个符合 spec 的时刻。
// spec 格式：
//  1. HH:MM                今天或明天该_wall-clock_
//  2. Mon Tue Wed Thu Fri Sat Sun HH:MM   下周几的该时刻
func Next(now time.Time, spec string) (time.Time, error) {
	spec = strings.TrimSpace(spec)
	parts := strings.Fields(spec)

	switch len(parts) {
	case 1: // HH:MM
		return nextClock(now, parts[0])
	case 2: // Weekday HH:MM
		//wd, ok := weekdays[parts[0]]
		wd, ok := weekDay(parts[0])
		if !ok {
			return time.Time{}, errors.New("invalid weekday")
		}
		return nextWeekdayClock(now, wd, parts[1])
	default:
		return time.Time{}, errors.New("format: HH:MM  or  Weekday HH:MM")
	}
}

// ----------------------------------------------------------
// 解析 HH:MM
func parseClock(s string) (hour, min int, err error) {
	if len(s) != 5 || s[2] != ':' {
		return 0, 0, errors.New("clock must be HH:MM")
	}
	hour, err = strconv.Atoi(s[:2])
	if err != nil || hour < 0 || hour > 23 {
		return 0, 0, errors.New("invalid hour")
	}
	min, err = strconv.Atoi(s[3:])
	if err != nil || min < 0 || min > 59 {
		return 0, 0, errors.New("invalid minute")
	}
	return hour, min, nil
}

func NextFixedInterval(t0 time.Time, interval time.Duration) time.Time {
	return t0.Add(interval).Truncate(interval)
}

// 下一个时间点
func NextInterval(nowTime, beginTime time.Time, interval time.Duration) time.Time {
	// 如果t0已经在t1之后，直接返回t0
	if beginTime.After(nowTime) {
		return beginTime
	}

	// 计算需要跨越的间隔数
	diff := nowTime.Sub(beginTime)
	intervals := (diff + interval + 1) / interval

	return beginTime.Add(time.Duration(intervals) * interval)
}

// 最近的一个时间点, 四舍五入到最近的一个时间点
func RoundIntervalAt(nowTime, beginTime time.Time, interval time.Duration) time.Time {
	if !beginTime.Before(nowTime) {
		return beginTime
	}

	// 计算需要跨越的间隔数
	diff := nowTime.Sub(beginTime).Round(interval)
	intervals := diff / interval

	return beginTime.Add(time.Duration(intervals) * interval)
}

// 下一个“当天或次日”的 HH:MM
func nextClock(t time.Time, clock string) (time.Time, error) {
	h, m, err := parseClock(clock)
	if err != nil {
		return time.Time{}, err
	}
	// 今天的同一时刻
	today := time.Date(t.Year(), t.Month(), t.Day(), h, m, 0, 0, t.Location())
	if !today.Before(t) { // 今天还没过
		return today, nil
	}
	// 已过了，取明天
	return today.AddDate(0, 0, 1), nil
}

// 下一个“指定星期几+HH:MM”
func nextWeekdayClock(t time.Time, wantWeekday time.Weekday, clock string) (time.Time, error) {
	h, m, err := parseClock(clock)
	if err != nil {
		return time.Time{}, err
	}

	// 先算“本周”那一天
	daysAhead := int(wantWeekday - t.Weekday())
	if daysAhead < 0 { // 已过了，下周
		daysAhead += 7
	}
	candidate := time.Date(t.Year(), t.Month(), t.Day(), h, m, 0, 0, t.Location()).AddDate(0, 0, daysAhead)

	// 如果刚好等于 t，也再跳一周（避免返回自身）
	if !candidate.After(t) {
		candidate = candidate.AddDate(0, 0, 7)
	}
	return candidate, nil
}

var weekdays = map[string]time.Weekday{
	"Mon": time.Monday,
	"Tue": time.Tuesday,
	"Wed": time.Wednesday,
	"Thu": time.Thursday,
	"Fri": time.Friday,
	"Sat": time.Saturday,
	"Sun": time.Sunday,
}

func weekDay(abbr string) (time.Weekday, bool) {
	var wd time.Weekday
	switch abbr {
	case "Mon":
		wd = time.Monday
	case "Tue":
		wd = time.Tuesday
	case "Wed":
		wd = time.Wednesday
	case "Thu":
		wd = time.Thursday
	case "Fri":
		wd = time.Friday
	case "Sat":
		wd = time.Saturday
	case "Sun":
		wd = time.Sunday
	default:
		return time.Sunday, false
	}
	return wd, true
}
